﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using SpectrumAnalyser.Config.Contract;
using SpectrumAnalyser.Controls.Wpf.ViewModels;
using SpectrumAnalyser.Data.Files.SpectrumDatabaseContracts.Repository;
using SpectrumAnalyser.Data.Files.SpectrumFileReaderContracts;
using SpectrumAnalyser.ModelObjects;
using SpectrumAnalyser.Shell.Commands;
using SpectrumAnalyser.Shell.Controls;
using SpectrumAnalyser.Shell.Controls.SpectrumDbConnections;
using SpectrumAnalyser.Shell.Helpers;
using SpectrumAnalyser.Shell.ViewModels;
using SpectrumAnalyser.Shell.ViewModels.Configuration;
using WindowStartupLocation = System.Windows.WindowStartupLocation;
using MahApps.Metro.Controls;
using MenuItem = System.Windows.Controls.MenuItem;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
using SaveFileDialog = Microsoft.Win32.SaveFileDialog;
using Microsoft.Practices.Unity;

namespace SpectrumAnalyser.Shell
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        private readonly IDictionary<TabItem, GraphicRenderControl> _openedTabs = new Dictionary<TabItem, GraphicRenderControl>();
        private readonly IDictionary<TabItem, SpectrumFileViewModel> _openedFiles = new Dictionary<TabItem, SpectrumFileViewModel>();
        private readonly ICommand _closeTabCommand;

        public ObservableCollection<SpectrumFileGroupViewModel> GroupFiles { get; set; }

        public MainWindow(IUnityContainer container)
        {
            InitializeComponent();

            
            try
            {
                var configService = container.Resolve<IConfigService>();
                configService.Ping();
                var i = configService.GetInstances();
            }
            catch (Exception e)
            {
                
            }

            FilesHistoryList.DataContext = this;
            RenderControlTabs.DataContext = this;
            GroupFiles =
                new ObservableCollection<SpectrumFileGroupViewModel>(
                    App.AppConfigHelper.GetFilesHistory().Select(x => new SpectrumFileGroupViewModel(x)));

            App.BusyIndicator = BusyIndicatorRoot;

            _closeTabCommand = new DelegateCommand(CloseTab);
        }

        public ICommand CloseTabCommand
        {
            get { return _closeTabCommand; }
        }

        public void CloseTab(object state)
        {
            if (state is TabItem)
            {
                RenderControlTabs.Items.Remove(state);

                _openedFiles.Remove(state as TabItem);
                _openedTabs.Remove(state as TabItem);
            }
        }

        #region Commands
        private void RefreshGraphicCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (!(e.Parameter is TreeViewItem)) return;
            var file = (e.Parameter as TreeViewItem).DataContext as SpectrumFileViewModel;

            RenderSpectrumFileViewModel(file, false);
        }

        private void DeleteFileFromHistoryCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (!(e.Parameter is TreeViewItem)) return;
            var file = (e.Parameter as TreeViewItem).DataContext as SpectrumFileViewModel;

            GroupFiles.Where(x => file != null && x.ItemsSource.Contains(file)).All(x => x.ItemsSource.Remove(file));
            App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
        }

        #endregion

        private void ManageSpectrumDB(object sender, RoutedEventArgs e)
        {
            var dbConnections = App.UnityContainer.Resolve<ManageSpectrumDbConnectionsWindow>();
            dbConnections.Connections =
                App.AppConfigHelper.GetSpectrumDbConnection().Select(x => new SpectrumDbConnectionViewModel(x)).ToList();
            dbConnections.Owner = this;
            dbConnections.WindowStartupLocation = WindowStartupLocation.CenterOwner;

            dbConnections.ShowDialog();
            if (dbConnections.DialogResult.HasValue && dbConnections.DialogResult.Value)
            {
                App.AppConfigHelper.UpdateSpectrumDbConnection(
                    dbConnections.Connections.Select(x => x.ToSpectrumDbConnection()).ToList());
            }
        }

        private void NewSpectrumGroup(object sender, RoutedEventArgs e)
        {
            var dialog = new CreateItemDialog { WindowStartupLocation = WindowStartupLocation.CenterOwner, Owner = this };
            dialog.OnValidateItemName += name => GroupFiles.All(x => x.Header != name) ? string.Empty : "Object with the same name already exists";

            dialog.ShowDialog();
            if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
            {
                GroupFiles.Add(new SpectrumFileGroupViewModel {Id = Guid.NewGuid(), Header = dialog.Item.ItemName});
            }

            App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
        }

        private void AddSpectrumFiles(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog();
            var fileReaders = App.UnityContainer.ResolveAll<ISpectrumFileReader>();

            /*var filters =
                fileReaders.Select(
                    fileReader =>
                    string.Format("{0} ({1})", fileReader.SpectrumFileReaderType,
                                  string.Join(";", fileReader.AcceptableExtensions))).ToList();*/
            dialog.Filter = "Defailr(*.txt)|*.*";

            if (dialog.ShowDialog() == true)
            {
                var itemsDialog = new ItemListDialog
                                      {
                                          WindowStartupLocation = WindowStartupLocation.CenterOwner,
                                          Owner = this,
                                          Width = 400
                                      };
                itemsDialog.ItemList.ItemsSource = GroupFiles;
                itemsDialog.ItemList.DisplayMemberPath = "Header";
                itemsDialog.ShowDialog();
                if (itemsDialog.DialogResult.HasValue && itemsDialog.DialogResult.Value && itemsDialog.ItemList.SelectedItem is SpectrumFileGroupViewModel)
                {
                    var id = (itemsDialog.ItemList.SelectedItem as SpectrumFileGroupViewModel).Id;
                    var group = GroupFiles.FirstOrDefault(x => x.Id == id);

                    var fileReader = fileReaders.ElementAt(dialog.FilterIndex - 1);

                    var spectrumFile = fileReader.ReadSpectrumFile(new FileInfo(dialog.FileName));
                    if (!ReferenceEquals(group, null) && !ReferenceEquals(spectrumFile, null))
                    {
                        group.ItemsSource.Add(new SpectrumFileViewModel(spectrumFile));
                    }
                    App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
                }
            }
        }

        private void RenameSpectrumGroup(object sender, RoutedEventArgs e)
        {
            if (!(sender is MenuItem) || !((sender as MenuItem).DataContext is SpectrumFileGroupViewModel))
            {
                return;
            }

            var groupViewModel = ((sender as MenuItem).DataContext as SpectrumFileGroupViewModel);

            var dialog = new CreateItemDialog {WindowStartupLocation = WindowStartupLocation.CenterOwner, Owner = this};
            dialog.OnValidateItemName += name => GroupFiles.All(x => x.Header != name) ? string.Empty : "Object with the same name already exists";

            dialog.Item.ItemName = groupViewModel.Header;
            dialog.ShowDialog();
            if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
            {
                GroupFiles.First(x => ReferenceEquals(x, groupViewModel)).Header = dialog.Item.ItemName;
            }

            App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
        }

        private void RemoveSpectrumGroup(object sender, RoutedEventArgs e)
        {
            if (!(sender is MenuItem) || !((sender as MenuItem).DataContext is SpectrumFileGroupViewModel))
            {
                return;
            }

            var groupViewModel = ((sender as MenuItem).DataContext as SpectrumFileGroupViewModel);

            if (GroupFiles.Count() == 1)
            {
                GroupFiles.Clear();
            }
            else
            {
                GroupFiles.Remove(groupViewModel);
            }

            App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
        }

        private void NewSpectrumInGroup(object sender, RoutedEventArgs e)
        {
            if (!(sender is MenuItem) || !((sender as MenuItem).DataContext is SpectrumFileGroupViewModel))
            {
                return;
            }
            var groupViewModel = ((sender as MenuItem).DataContext as SpectrumFileGroupViewModel);
            if (ReferenceEquals(groupViewModel,null))
            {
                return;
            }

            var dialog = new OpenFileDialog();
            var fileReaders = App.UnityContainer.ResolveAll<ISpectrumFileReader>();

            /*var filters =
                fileReaders.Select(
                    fileReader =>
                    string.Format("{0} ({1})", fileReader.SpectrumFileReaderType,
                                  string.Join(";", fileReader.AcceptableExtensions))).ToList();*/
            dialog.Filter = "Defailr(*.txt)|*.*";

            if (dialog.ShowDialog() == true)
            {
                var fileReader = fileReaders.ElementAt(dialog.FilterIndex - 1);

                var spectrumFile = fileReader.ReadSpectrumFile(new FileInfo(dialog.FileName));
                if (!ReferenceEquals(spectrumFile, null))
                {
                    groupViewModel.ItemsSource.Add(new SpectrumFileViewModel(spectrumFile));

                    App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
                }
            }
        }

        private void RenderSpectrumFileViewModel(SpectrumFileViewModel model, bool searchLines)
        {
            App.BusyIndicator.IsActive = true;

            var worker = new BackgroundWorker();
            worker.DoWork += (o, e1) => PrepareSpectrumViewModel(model, searchLines);
            worker.RunWorkerCompleted += (o, e1) => Dispatcher.BeginInvoke((Action) (() =>
                {
                    StatusMessage("Drawing chart...");
                    _openedTabs[RenderControlTabs.SelectedItem as TabItem].RenderFiles(new List<SpectrumFileViewModel> { model });
                    App.BusyIndicator.IsActive = false;
                    StatusMessage("Ready");
                }));

            worker.RunWorkerAsync();
            worker.Dispose();
        }

        private void PrepareSpectrumViewModel(SpectrumFileViewModel model, bool searchLines)
        {
            StatusMessage("Calculating wavelet...");
            //IList<IDataPoint> waveletData = WaveletHelper.WaveletTransform(model, App.AppConfigHelper.GetPeaksSearchSettings()).DataPoints;
            StatusMessage("Calculating peaks...");
            //var peaks = WaveletHelper.GetPeaksWaveletTransform(model.DataPoints, WaveletHelper.PeaksFinder(waveletData, App.AppConfigHelper.GetPeaksSearchSettings())).ToList();
            SpectrumSearchModel searchModel = WaveletHelper.PeaksFinder(model.DataPoints, App.AppConfigHelper.GetPeaksSearchSettings());

            if (searchLines)
            {
                var connections = App.AppConfigHelper.GetSpectrumDbConnection().Where(x => x.IsUsed);
                var linesSearchSettings = App.AppConfigHelper.GetLinesSearchSettings();
                foreach (var spectrumDbConnectionViewModel in connections)
                {
                    var repository = Activator.CreateInstance(spectrumDbConnectionViewModel.Type)
                                     as ISpectrumLinesRepository;
                    if (repository != null)
                    {
                        searchModel.PeaksCollection = repository.FillMatchedPeaks(searchModel.PeaksCollection,
                                                                                  spectrumDbConnectionViewModel,
                                                                                  linesSearchSettings);
                    }
                }
            }

            model.Peaks = new ObservableCollection<Peak>(searchModel.PeaksCollection);
            model.Wavelet = new ObservableCollection<SpectrumDataPointViewModel>(searchModel.WaveletData.Cast<SpectrumDataPointViewModel>());
        }

        private void FilesHistoryFileMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!(sender is FrameworkElement) || !((sender as FrameworkElement).DataContext is SpectrumFileViewModel))
            {
                return;
            }

            var viewFile = ((sender as FrameworkElement).DataContext as SpectrumFileViewModel);

            var renderControl = new GraphicRenderControl();
            if (!_openedFiles.Values.Contains(viewFile))
            {
                var tab = new TabItem {Header = viewFile.FileName, Content = renderControl};
                RenderControlTabs.Items.Add(tab);
                RenderControlTabs.SelectedItem = tab;
                _openedTabs.Add(tab, renderControl);
                _openedFiles.Add(tab, viewFile);

                RenderSpectrumFileViewModel(viewFile, false);
            }else
            {
                var selectedTab = _openedFiles.FirstOrDefault(x => ReferenceEquals(x.Value, viewFile)).Key;
                RenderControlTabs.SelectedItem = selectedTab;
            }
        }

        private void RemoveSpectrumFile(object sender, RoutedEventArgs e)
        {
            if (!(sender is FrameworkElement) || !((sender as FrameworkElement).DataContext is SpectrumFileViewModel))
            {
                return;
            }

            var viewFile = ((sender as FrameworkElement).DataContext as SpectrumFileViewModel);

            foreach (var spectrumFileGroupViewModel in GroupFiles)
            {
                if (spectrumFileGroupViewModel.ItemsSource.Contains(viewFile))
                {
                    spectrumFileGroupViewModel.ItemsSource.Remove(viewFile);
                }
            }

            App.AppConfigHelper.UpdateFileHistory(GroupFiles.Select(x => x.ToSpectrumFileGroup()).ToList());
        }

        private void SettingsClick(object sender, RoutedEventArgs e)
        {
            var settingsWindow = new SettingsDialog
            {
                Owner = this,
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Settings = App.AppConfigHelper.GetPeaksSearchSettings() ?? new PeakSearchSettings()
            };

            settingsWindow.ShowDialog();

            if (settingsWindow.DialogResult.HasValue && settingsWindow.DialogResult.Value)
            {
                App.AppConfigHelper.UpdatePeaksSearchSettings((PeakSearchSettings)settingsWindow.Settings);
                RenderSpectrumFileViewModel(_openedFiles[RenderControlTabs.SelectedItem as TabItem], false);
            }
        }

        private void SearchPeaksClick(object sender, RoutedEventArgs e)
        {
            RenderSpectrumFileViewModel(_openedFiles[RenderControlTabs.SelectedItem as TabItem], false);
        }

        private void SearchLinesClick(object sender, RoutedEventArgs e)
        {
            var graphicControl = _openedTabs[RenderControlTabs.SelectedItem as TabItem];
            var selectedPeaks = graphicControl.Peaks.Where(x => x.IsVisible).ToList();

            StatusMessage("Searching spectrum lines...");
            App.BusyIndicator.IsActive = true;

            var worker = new BackgroundWorker();
            worker.DoWork += (o, e1) =>
                {
                    selectedPeaks = App.AppConfigHelper
                                       .GetSpectrumDbConnection().Where(x => x.IsUsed)
                                       .Aggregate(selectedPeaks,
                                                  (current, spectrumDbConnectionViewModel) =>
                                                      {
                                                          var spectrumLinesRepository =
                                                              Activator.CreateInstance(
                                                                  spectrumDbConnectionViewModel.Type) as
                                                              ISpectrumLinesRepository;
                                                          return spectrumLinesRepository != null
                                                                     ? spectrumLinesRepository
                                                                           .FillMatchedPeaks(current,
                                                                                             spectrumDbConnectionViewModel,
                                                                                             App.AppConfigHelper
                                                                                                .GetLinesSearchSettings()
                                                                           ).ToList()
                                                                     : null;
                                                      });
                };
            worker.RunWorkerCompleted += (o, e1) => Dispatcher.BeginInvoke((Action) (() =>
                {
                    selectedPeaks.AddRange(graphicControl.Peaks.Where(x => !x.IsVisible).ToList());
                    graphicControl.Peaks = new ObservableCollection<Peak>(selectedPeaks);
                    App.BusyIndicator.IsActive = false;
                    StatusMessage("Ready");
                }));

            worker.RunWorkerAsync();
            worker.Dispose();
        }

        private void LinesSearchSettingsClick(object sender, RoutedEventArgs e)
        {
            var settingsWindow = new SettingsDialog
            {
                Owner = this,
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Settings = App.AppConfigHelper.GetLinesSearchSettings()
            };

            settingsWindow.ShowDialog();

            if (settingsWindow.DialogResult.HasValue && settingsWindow.DialogResult.Value)
            {
                App.AppConfigHelper.UpdateLinesSearchSettings((LinesSearchSettings)settingsWindow.Settings);
            }
        }

        private void StatusMessage(string message)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                StatusMessageTextBlock.Text = message;
            }));
        }

        private void ExportImage(object sender, RoutedEventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();

            dialog.Filter = "PNG file (*.png)|*.png";
            dialog.RestoreDirectory = true;
            dialog.OverwritePrompt = true;

            var dialogResult = dialog.ShowDialog();
            if (dialogResult.Value)
            {
                using (var stream = dialog.OpenFile())
                {
                    _openedTabs[RenderControlTabs.SelectedItem as TabItem].ExportImage(stream);
                }
            }
        }

        private void ResetZoom(object sender, RoutedEventArgs e)
        {
            _openedTabs[RenderControlTabs.SelectedItem as TabItem].ResetZoom();
        }
    }
}
